#include "ShowImage.h"


bool ShowImage::showPhoto(const char *filename,bool showErrFlag)
{
	bool flag;
	if(strstr(filename,".bmp"))
	{
		displayBMP(filename);
		flag = true;
	}
	else if(strstr(filename,".jpg"))
	{
		displayJPG(filename);
		flag = true;
	}
	else
	{
		if(showErrFlag)
		{
			printf("showPhoto() show %s error\n",filename);
		}
		flag = false;
	}

	return flag;
}

// 遍历 Dirname里面的bmp和jpg图片，循环播放一次，间歇为internel
void ShowImage::showAllPhotoInDir(const char *Dirname ,int interval)
{
	struct dirent *ptr;
	DIR * dirFp = opendir(Dirname);
	if(dirFp == NULL)
	{
		printf("showAllPhotoInDir() opendir error\n");
		return ;
	}
	// 不进入目录的话，下面就需要使用决定路径了
	chdir(Dirname);
	// 其实不需要另外保存到二维数组中了
	while( (ptr = readdir(dirFp)) !=NULL)
	{
		// printf("filename = %s\n",ptr->d_name);
		if(showPhoto(ptr->d_name,false) == true)
		{
			// 成功显示图片，延时5s
			sleep(interval);
		}
	}
	// 如果不close会出现内存泄漏
	closedir(dirFp);

}



//=======================================
//+传入图片文件名还有大小，得到图片文件中存储的JPG数据的信息
char * ShowImage::readjpg(char const *filename,int size)
{
	//只读打开这个文件
	int fd_photo = open(filename,O_RDONLY);
	if(fd_photo == -1)
	{
		printf("打开图片失败\n");
		exit(0);
	}
	
	//用malloc因为其他函数也要使用，要长期存在
	//而且我们可以控制它的free，所以不用static
	char *jpg_buffer = (char *)calloc(1,size);

	int n =0;
	//把打开的图片文件的数据读取到jpg_buf中存起来
	while(size > 0)
	{
		//n返回的是读取到的字节数,写的区域是jpg_buffer+n
		//那么n是下一次的偏移值，使用完参数，结束函数才有n的输出
		//如果多次读，好像就有问题，只读一次或者两次才不出错
		n = read(fd_photo,jpg_buffer+n,size);
		size = size - n;	//还剩下size个字节
	}
	
	close(fd_photo);
	//把这篇存储信息的空间返回；
	return jpg_buffer;
}


// 板子的图片位置： /IOT/jpg_photo
void ShowImage::displayJPG(const char *filename)		//函数的功能就是，我把图片名字给你，你帮我显示
{
	//获取图片的文件信息，才能得到大小，作为参数给readjpg
	struct stat info;
	bzero(&info, sizeof(info));

	//得到文件信息，存入结构体info中，主要是大小
	stat(filename, &info);
	
	
	//+得到图片中的RGB数据，在jpg_buffer中
	char *jpg_buffer = readjpg(filename,info.st_size);

	// 声明解压缩结构体，以及错误管理结构体
	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;

	// 使用缺省的出错处理来初始化解压缩结构体
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);

	// 配置该cinfo，使其从jpg_buffer中读取jpg_size个字节
	// 这些数据必须是完整的JPEG数据
	jpeg_mem_src(&cinfo, (const unsigned char*)jpg_buffer, info.st_size);


	// 读取JPEG文件的头，并判断其格式是否合法
	//int ret = jpeg_read_header(&cinfo, true);
	int ret = jpeg_read_header(&cinfo, (boolean)1);
	if(ret != 1)
	{
		fprintf(stderr, "[%d]: jpeg_read_header failed: "
			"%s\n", __LINE__, strerror(errno));
		exit(1);
	}

	// 开始解压
	jpeg_start_decompress(&cinfo);

	// 将图片的宽、高、深度信息保存起来
	
	//***这里得到的是解压之后的信息
	//***解压之后，由jpg格式变为RGB格式，变大了
	struct imginfo imageinfo;
	imageinfo.width      = cinfo.output_width;
	imageinfo.height     = cinfo.output_height;
	imageinfo.pixel_size = cinfo.output_components;

	int row_stride = imageinfo.width * imageinfo.pixel_size;

	// ***根据图片的尺寸大小，分配一块相应的内存rgb_buffer
	// ***用来存放从jpg_buffer解压出来的图像数据
	unsigned long rgb_size;
	unsigned char *rgb_buffer;
	rgb_size   = imageinfo.width * imageinfo.height * imageinfo.pixel_size;
	rgb_buffer = (unsigned char *)calloc(1, rgb_size);

	// 循环地将图片的每一行读出并解压到bmp_buffer中
	int line = 0;
	while(cinfo.output_scanline < cinfo.output_height)
	{
		unsigned char *buffer_array[1];
		buffer_array[0] = rgb_buffer +
				(cinfo.output_scanline) * row_stride;
		jpeg_read_scanlines(&cinfo, buffer_array, 1);
	}

	// 解压完了，将jpeg相关的资源释放掉
 	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);
	free(jpg_buffer);

	//将rgb_buffer中的数据，妥善地放到lcd上去显示
	
	//【1.】打开lcd文件
	int lcd = open("/dev/fb0",O_RDWR);
	
	//【1.5】获得LCD屏幕的参数
	
	struct fb_var_screeninfo vinfo;		//定义一个结构体变量
	bzero(&vinfo,sizeof(vinfo));
	
	//将参数值读到vinfo结构体变量中
	ioctl(lcd,FBIOGET_VSCREENINFO,&vinfo);
	
	//简化变量名字，得到lcd的长宽，深度
	int lcd_w   = vinfo.xres;
	int lcd_h   = vinfo.yres;
	int lcd_bpp = vinfo.bits_per_pixel;
	
	//简化变量名字,图片文件的尺寸
	int w = imageinfo.width;
	int h = imageinfo.height;


#if 0
	//【2.】映射一片内存
	// 映射内存，与LCD有关
	char *p = (char *)mmap(NULL, lcd_w * lcd_h * lcd_bpp/8, PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0);
	
	

	//【3.】把数据放到映射的内存里面
	//将rgb_buffer中存储的RGB数据，放入映射内存中
	//rgb数据一个像素点3个字节，lcd4个字节，包含透明度
	//printf("this is new\n");
	for(int j=0;j<480 && j<h;j++)	//不超过lcd或图片的尺寸
	{
		for(int i=0;i<800 && i<w;i++)
		{
			//p写三个，但是下一个像素点，p要跳4个字节
			//offset是偏移量的位数，所以要除以8得到偏移量的字节数
			
			//p(映射内存)一个像素点4个字节，一行就800*4，偏移量交给vinfo.xxx.offset帮我们算
			//偏移量定位到对应颜色，但是我们在显示颜色的时候，不是已经可以知道映射内存按{透明度，R，G，B分吗}？
			
			//最后一个参数是1，代表每次写1个字节，每个像素点写3次
			
			memcpy(p+4*i+800*4*j+vinfo.red.offset/8,rgb_buffer+ 3*i +w*3*j +0,1);
			memcpy(p+4*i+800*4*j+vinfo.green.offset/8,rgb_buffer+ 3*i +w*3*j +1,1);
			memcpy(p+4*i+800*4*j+vinfo.blue.offset/8,rgb_buffer+ 3*i +w*3*j +2,1);
			
			// 下面是原來打算用int的，但是處理圖片的便宜有點麻煩，暫時未OK，to do
/*
			//memcpy(p+i+800*j,rgb_buffer+ 3*i +w*3*j,3);
			//int tmp = ((*(rgb_buffer+ 3*i +w*3*j))>>16) | ((*(rgb_buffer+ 3*i +w*3*j +1)) >> 8) | ((*(rgb_buffer+ 3*i +w*3*j +2))) ;
			 
			//int tmp = (  ((*(rgb_buffer+ 3*i +w*3*j+3))&0xff)>>16) | (  ((*(rgb_buffer+ 3*i +w*3*j +2) )&0xff) >> 8) | (  ((*(rgb_buffer+ 3*i +w*3*j +1))&0xff)   ) ;
			int tmp = ( ((UCHAR)*(rgb_buffer+ 3*i +w*3*j))>>16) | ( (UCHAR)(*(rgb_buffer+ 3*i +w*3*j +1)) >> 8) | ( (UCHAR)(*(rgb_buffer+ 3*i +w*3*j +2))) ;
			memcpy(p+i+800*j,&tmp,3);
*/		
		}
	}

#endif

#if 1

	//  vinfo.red.offset = 16
	//  vinfo.green.offset = 8
	//  vinfo.blue.offset = 0
	// printf(" vinfo.red.offset = %d\n",vinfo.red.offset);		
	// printf(" vinfo.green.offset = %d\n",vinfo.green.offset);	
	// printf(" vinfo.blue.offset = %d\n",vinfo.blue.offset);	

	int *p = (int *)mmap(NULL, lcd_w * lcd_h * lcd_bpp/8, PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0);
	for(int j=0;j<480 && j<h;j++)	//不超过lcd或图片的尺寸
	{
		for(int i=0;i<800 && i<w;i++)
		{

#if 1
			UCHAR red = *(rgb_buffer+ 3*i +w*3*j +0);
			UCHAR green = *(rgb_buffer+ 3*i +w*3*j +1);
			UCHAR blue = *(rgb_buffer+ 3*i +w*3*j +2);
			//UINT32 tmp = (UINT32)( (((UINT32) 0)>>24) + ( ( (UINT32) red) >> 16 ) + ( ( (UINT32) green) >> 8 ) + ( ( (UINT32) blue) >> 0 )  );
			// 上面的都不行
			UINT32 tmp = red;
			tmp <<= 8;
			tmp |= green;
			tmp <<= 8;
			tmp |= blue;

			// [透明度，R，G，B]
			memcpy(p+i+800*j,&(tmp),4);
#else
			// OK!
			UNI_U32INT_4UCHAR tmp;
			tmp.UCharData[3] = 0;
			tmp.UCharData[2] = *(rgb_buffer+ 3*i +w*3*j+0);
			tmp.UCharData[1] = *(rgb_buffer+ 3*i +w*3*j+1);
			tmp.UCharData[0] = *(rgb_buffer+ 3*i +w*3*j+2);
			// [透明度，R，G，B]
			memcpy(p+i+800*j,&(tmp.U32Data),4);
#endif
		}
	}

#endif


	//【4.】关闭文件，释放映射内存
	munmap(p,lcd_w * lcd_h * lcd_bpp/8);
	free(rgb_buffer);
	close(lcd);
	
	
}



void ShowImage::displayBMP(const char *filename)
{
	// 1, 打开图片
	int fd = open(filename,O_RDONLY);
	if(fd == -1)
	{
		printf("读取文件失败\n");
		exit(0);
	}

	// 2, 读取头部信息，从图片文件中，头部信息包含图片大小，深度
	struct bitmap_header head;
	struct bitmap_info   info;

	bzero(&head, sizeof(head));
	bzero(&info, sizeof(info));

	read(fd, &head, sizeof(head));
	read(fd, &info, sizeof(info));


	// 3, 准备LCD设备
	int lcd = open("/dev/fb0",O_RDWR);
	if(lcd == -1)
	{
		printf("打开lcd文件失败\n");
		exit(0);
	}
	
	//3.1获取lcd屏幕的参数大小，其实就是800*480
	struct fb_var_screeninfo vinfo;
	bzero(&vinfo,sizeof(vinfo));
	
	ioctl(lcd,FBIOGET_VSCREENINFO,&vinfo);

	//申请映射内存			//宽        高		深度（位）/8 = 深度（字节）		
	char *p = (char *)mmap(NULL,vinfo.xres*vinfo.yres*vinfo.bits_per_pixel/8,
					PROT_READ | PROT_WRITE,MAP_SHARED,
					lcd,0);
	
	if(p == MAP_FAILED)
	{
		printf("映射内存失败\n");
		exit(0);
	}
	
	
	// 4, 读取图片的RGB数据，直接放入显存
	int width  = info.width;
	int height = info.height;
	int bpp    = info.bit_count;
	

	//printf("bmp图片的尺寸是%d*%d，深度是%d",width,height,bpp);
	
	
	//使用一片动态内存存储rgb信息
	char *rgb_buffer = (char *)calloc(1,width*height*bpp/8);
	read(fd,rgb_buffer,width*height*bpp/8);
	
	
	
	
	for(int j=0;j<vinfo.yres && j<height;j++)	//不能超过lcd屏幕大小或者是图片大小
	{

		for(int i=0;i<vinfo.xres && i<width;i++)
		{		
						//在动态内存之中获取信息			//一行像素点的字节数
			memcpy(p+i*4+(vinfo.xres*vinfo.bits_per_pixel/8)*j,rgb_buffer+i*3+(width*bpp/8)*j,3);
		}

	}
	
	munmap(p,vinfo.xres*vinfo.yres*vinfo.bits_per_pixel/8);
	if(rgb_buffer!=NULL)
	{
		free(rgb_buffer);
	}
	close(fd);
	close(lcd);
	
	
}
